// 泛型
// 泛型（Generics）是指在定义函数、接口或类的时候，不预先指定具体的类型，而在使用的时候再指定类型的一种特性
// 泛型T作用域只限于函数内部使用


// 1.泛型函数
namespace a {
    function createArray<T>(length: number, value: T): Array<T> {
        let result: T[] = [];
        for (let i = 0; i < length; i++) {
          result[i] = value;
        }
        return result;
      }
    console.log(createArray<string>(3,'x'))
}

// 2.泛型类
namespace b {
    class MyArr<T> {
        private list:T[] = []
        add(value:T){
            this.list.push(value)
        }
        getMax():T{
            return this.list[0]
        }
    }
    let arr = new MyArr<number>()
    arr.add(1)
    arr.add(2)
    arr.add(3)
    console.log(arr.getMax()); //1
} 
// 3.泛型与 new
namespace c {
    function factory<T>(type: {new():T}): T { //new()代表类的构造函数
        return new type(); // This expression is not constructable.
    }
    class Person{}
    factory<Person>(Person)
} 
// 4.泛型接口
namespace d {
    interface Calculate{
        <T>(a:T,b:T):T
    }
    let add:Calculate = function<T>(a:T,b:T){
        return a;
    }
    add<number>(1,2);
    interface Calculate1<T>{
        (a:T,b:T):T
    }
    let add1:Calculate1<number> = function(a:number,b:number):number{
        return a;
    }
    add1(1,2);
    interface Calculate2<T>{
        <U>(a:T,b:T):U
    }
    let add2:Calculate2<number> = function<U>(a:number,b:number):U{
        return a as any;
    }
    add2<number>(1,2);
} 
// 5.多个类型参数:泛型可以有多个
namespace e {
    function swap<A,B>(tuple:[A,B]):[B,A]{
        return [tuple[1],tuple[0]];
    }
    let swapped = swap<string,number>(['a',1]);
    console.log(swapped);
    console.log(swapped[0].toFixed(2));
    console.log(swapped[1].length);
} 
// 6.默认泛型(类型推论)
namespace f {
    interface T1<T = string>{

    }
    type T2 = T1
}
// 7.泛型约束
// 在函数中使用泛型的时候，由于预先并不知道泛型的类型，所以不能随意访问相应类型的属性或方法。
namespace e {
      interface LengthWise{
        length:number
      }
      //非常非常非常重要
    //   可以让泛型继承一个接口或者类，那么使用泛型时候必须要有继承过来的方法或属性
      function logger2<T extends LengthWise>(val: T) {
        console.log(val.length);
      }
      let obj  = {
        length:10
      }
      type Obj = typeof obj;
      logger2<Obj>(obj);
      //判断兼容不兼容跟extends继承没有一点关系 ,只看形状 有没有对应的属性
      class GrandFather {
        grandFather:string = '1'
      }
      class Father extends GrandFather {
        father:string ='2'
      }
      class Child extends Father{
        child:string = '3'
      }
      //约束  
      //或说T能赋值给Father
      //T是Father的子类型
      function get<T extends Father>(){
      
      }
      get<GrandFather>()
    //   let father = new Father();
    //   let child = new Child();
    //   father = child;
}
namespace h {
    // 泛型还可以继承联合类型
    interface Calute {
        <T extends (number|string)>(a:T,b:T):T
    }
    let sum:Calute = function<T extends (string|number)>(a:T,b:T):T{
        return a
    }
    sum<(string)>('1','2')
}
// 10.泛型类型别名
// 泛型类型别名可以表达更复杂的类型
namespace j {
    type Cart<T> = {list:T[]} | T[];
    let c1:Cart<string> = {list:['1']};
    let c2:Cart<number> = [1];
}

// 泛型接口 vs 泛型类型别名
// 接口创建了一个新的名字，它可以在其他任意地方被调用。而类型别名并不创建新的名字，例如报错信息就不会使用别名
// 类型别名不能被 extends和 implements,这时我们应该尽量使用接口代替类型别名
// 当我们需要使用联合类型或者元组类型的时候，类型别名会更合适


namespace k {
    // type A = {a:number}|{b:string}
    // function fn<T extends A>(c:T):void{}
    // fn<{a:number}>({a:1})
    // type A = {a:number}&{b:string}
    // function fn<T extends A>(c:T):void{}
    // fn<{a:number,b:string}>({a:1,b:'2'})
}